#pragma once

#include <stdio.h>
#include <iostream>
#include <fstream>
#include <mutex>
#include<filesystem>
#include <unistd.h>
#include <memory>

namespace LogMoudle
{
    const std::string default_path = "./log";       // 默认日志路径
    const std::string default_filename = "log.txt"; // 默认日志文件名

    enum class LogLevel
    {
        DEBUG = 1,
        INFO = 2,
        WARNING = 3,
        ERROR = 4,
        FATAL = 5
    };
    // 日志等级转字符串:因为日志等级本身是一个枚举类型，会被替换成数值，但我们需要将其原本字符串显示出来，因此需要将其转换为字符串
    std::string LogLevel2Str(LogLevel level)
    {
        switch (level)
        {
        case LogLevel::DEBUG:
            return "DEBUG";
        case LogLevel::INFO:
            return "INFO";
        case LogLevel::WARNING:
            return "WARNING";
        case LogLevel::ERROR:
            return "ERROR";
        case LogLevel::FATAL:
            return "FATAL";
        };
        return "UNKNOWN";
    }

    class LogStrategy // 日志输出策略抽象接口类
    {
    public:
        virtual ~LogStrategy() = default;                     // 虚析构函数 保证基类的资源释放
        virtual void SyncLog(const std::string &message) = 0; // 纯虚函数，定义日志刷新逻辑
    };

    class ConsoleLogStrategy : public LogStrategy
    {
    public:
        void SyncLog(const std::string &message) override
        {
            std::lock_guard<std::mutex> lock(_mutex); // 加锁避免多线程竞争
            std::cout << message << std::endl;
        }

    private:
        std::mutex _mutex; // 线程同步锁
    };

    class FileLogStrategy : public LogStrategy
    {
    public:
        FileLogStrategy(const std::string &path = default_path, const std::string &filename = default_filename)
            : _path(path), _filename(filename)
        {
            if (!std::filesystem::exists(_path))
            {
                try
                {
                    std::filesystem::create_directories(_path);
                }
                catch (const std::filesystem::filesystem_error &e)
                {
                    std::cerr << e.what() << '\n';
                }
            }
        }

        void SyncLog(const std::string &message) override
        {
            std::lock_guard<std::mutex> lock(_mutex); // 加锁避免多线程竞争
            std::string fullPath = _path + "/" + _filename;
            std::ofstream ostream(fullPath, std::ios::app); // 追加方式写入
            if (ostream.is_open())
            {
                ostream << message << std::endl;
                ostream.close();
            }
        }

    private:
        std::string _path;
        std::string _filename;
        std::mutex _mutex; // 线程同步锁
    };

    class Logger
    {
    private:
        class LogMessage
        {

        public:
            LogMessage(LogLevel level, const std::string &fileName, int line, Logger &logger)
                : _time(getCurrTime()),
                  _level(level),
                  _pid(getpid()),
                  _tid(gettid()),
                  _fileName(fileName),
                  _line(line),
                  _logger(logger)
            {
                char buf[1024];
                // 开始拼接日志字符串: [2024-08-04 12:27:03] [DEBUG] [202938] [4459] [main.cc] [16]
                snprintf(buf, sizeof(buf), "[%s] [%s] [%d] [%d] [%s] [%d]",
                         _time.c_str(),
                         LogLevel2Str(_level).c_str(),
                         _pid,
                         _tid,
                         _fileName.c_str(),
                         _line);
                _logInfo = buf;
            }

            ~LogMessage()
            {
                // 析构函数: 析构时，将日志字符串刷新到⽂件中
                _logger._strategy->SyncLog(_logInfo);
            }
            // std::string message = std::format("[{}][{}][{}][{}][{}]", _time, LogLevel2Str(_level), _pid, _fileName, _line);
            template <typename T>
            LogMessage &operator<<(const T &value)
            {
                std::stringstream ssbuffer;
                ssbuffer << value;
                _logInfo += ssbuffer.str();
                return *this;
            }

            std::string getCurrTime()
            {
                // 时间格式: 2024-08-04 12:27:03
                time_t now = time(nullptr); // 获取当前的时间戳
                struct tm curr;
                localtime_r(&now, &curr); // 将时间戳转换为本地时间
                // 格式化时间
                char buffer[100];
                snprintf(buffer, sizeof(buffer), "%4d-%02d-%02d %02d:%02d:%02d",
                         curr.tm_year + 1900,
                         curr.tm_mon + 1,
                         curr.tm_mday,
                         curr.tm_hour,
                         curr.tm_min,
                         curr.tm_sec);
                return buffer;
            }

            std::string _time;     // 时间
            LogLevel _level;       // 日志等级
            int _pid;              // 本进程pid
            int _tid;              // 进程id
            std::string _fileName; // 文件名
            int _line;             // 行号
            std::string _logInfo;  // 一行完整的日志信息
            Logger &_logger;
        };

    public:
        Logger()
        {
            // 默认使用控制台日志策略
            _strategy = std::make_shared<ConsoleLogStrategy>();
        }

        // 主动设置日志刷新策略
        void EnableConsoleLogStrategy()
        {
            _strategy = std::make_shared<ConsoleLogStrategy>();
        }
        void EnableFileLogStrategy()
        {
            _strategy = std::make_shared<FileLogStrategy>();
        }

        // 重载():
        // 故意拷⻉，形成LogMessage临时对象，后续在被<<时，会被持续引⽤，
        // 直到完成输⼊，才会⾃动析构临时LogMessage，⾄此也完成了⽇志的显⽰或者刷新
        // 同时，形成的临时对象内包含独⽴⽇志数据
        // 未来采⽤宏替换，进⾏⽂件名和代码⾏数的获取
        LogMessage operator()(LogLevel type, std::string filename, int line)
        {
            return LogMessage(type, filename, line, *this);
        }

    private:
        std::shared_ptr<LogStrategy> _strategy;
    };

    Logger logger;
    #define LOG(level) logger(level,__FILE__,__LINE__)

};